/****************************************************
          File Name:pisces_socket.c
          Author: Abby Cin
          Mail: abbytsing@gmail.com
          Created Time: Tue 13 Jan 2015 09:47:01 AM CST
*****************************************************/

#include "pisces_socket.h"
#include <strings.h>

int pisces_unblock_socket(int sfd)
{
    int flags, res;

    flags = fcntl(sfd, F_GETFL, 0);
    if (flags == -1)
        return -1;
    flags |= O_NONBLOCK;
    res = fcntl(sfd, F_SETFL, flags);
    if (res == -1)
        return -1;

    return 0;
}

int pisces_socket(pisces_ctx_t ctx, socklen_t * addr_len)
{
    struct addrinfo *current;
    struct addrinfo hints;
    struct addrinfo *result = NULL;
    int sockfd = 0;
    int option = 1;

    bzero(&hints, sizeof(struct addrinfo));
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_family = AF_UNSPEC;        /* IPv4 or IPv6 */

    getaddrinfo(ctx.conf->host, ctx.conf->port, &hints, &result);

    /* traverse addrinfo list */
    for (current = result; current != NULL; current = current->ai_next) {
        sockfd = socket(current->ai_family, current->ai_socktype, current->ai_protocol);
        if (sockfd == -1)
            continue;
        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)) == -1) {
            close(sockfd);
            freeaddrinfo(result);
            return -1;
        }
        if (pisces_unblock_socket(sockfd) < 0) {
            close(sockfd);
            return -1;
        }
        if (bind(sockfd, current->ai_addr, current->ai_addrlen) == 0)
            break;
        close(sockfd);          /* failed in bind(), close it */
    }

    if (current != NULL) {
        if (listen(sockfd, ctx.conf->backlog) == -1) {
            freeaddrinfo(result);
            return -1;
        }
    }
    if (current != NULL && addr_len != NULL)
        *addr_len = current->ai_addrlen;        /* for latter use(get clients' info) */

    freeaddrinfo(result);
    return (current == NULL) ? -1 : sockfd;
}
